# Copyright 2009 Larry Resnik, Lasfraka-Elixir

#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.

#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.


from __future__ import division

import pyglet
from pyglet.gl import glPushMatrix, glTranslatef, glPopMatrix
try:
    import tiledtmxloader
except ImportError:
    raise ImportError, 'Level loading requires tiledtmxloader. Get it at ' \
        'http://www.pygame.org/project/1158/?release_id=2135'

COLLIDE_TYPE_NONE = 0
COLLIDE_TYPE_CIRC = 1
COLLIDE_TYPE_RECT = 2

# Tile properties. Based on the location of collide.png's images.
# Should collide.png be changed, this must reflect that exactly.
(TPROP_EMPTY,           # Tile 0
TPROP_LADDER,
TPROP_RESERVED_EMPTY2,
TPROP_RESERVED_EMPTY3,
TPROP_RESERVED_EMPTY4,  # Tile 5
TPROP_RESERVED_EMPTY5,
TPROP_RESERVED_EMPTY6,
TPROP_RESERVED_EMPTY7,
TPROP_RESERVED_EMPTY8,
TPROP_RESERVED_EMPTY9,
TPROP_THICK,            # Tile 10
TPROP_SLIPPERY,
TPROP_NO_WALLGRIP,
TPROP_CONVEYOR_LEFT,
TPROP_CONVEYOR_RIGHT,
TPROP_RESERVED_THICK15, # Tile 15
TPROP_RESERVED_THICK16,
TPROP_RESERVED_THICK17,
TPROP_RESERVED_THICK18,
TPROP_RESERVED_THICK19,
TPROP_RESERVED_THICK20, # Tile 20
TPROP_RESERVED_THICK21,
TPROP_RESERVED_THICK22,
TPROP_RESERVED_THICK23,
TPROP_RESERVED_THICK24,
TPROP_RESERVED_THICK25, # Tile 25
TPROP_RESERVED_THICK26,
TPROP_RESERVED_THICK27,
TPROP_RESERVED_THICK28,
TPROP_DAMAGE,
TPROP_LETHAL) = range(31)

class Level(object):
    """This is a class for storing the tile statistics and images.

    Loading the level can be postponed by not providing a filename to __init__.
    Draw all the tiles with draw(). Level retains its own set of OrderedGroups
    to handle each layer's drawing order.
    Access the individual tiles with get_at().
    """

    collideType = COLLIDE_TYPE_RECT

    def __init__(self, filename=None, batch=None):
        self.levelMap = None
        self.mapWidth = 0
        self.mapHeight = 0
        # This refers to the damage inflicted by damaging tiles.
        self.damage = 2
        if batch:
            self.batch = batch
        else:
            self.batch = pyglet.graphics.Batch()
        self.sprites = []
        # Assume there's only 1 background and the rest overlays it.
        # So objects may be put in to group 0 after all of this.
        self.groups = []
        self.load(filename)

    def _print_collide_grid(self):
        """Debugging method that displays the collide_grid."""
        for row in self.collide_grid:
            print(row)

    def _make_collide_grid(self):
        self.collide_grid = []
        id_index = 0
        collide_layer = self.levelMap.named_layers['collide']
        for y in range(0,collide_layer.pixel_height,self.levelMap.tileheight):
            current_grid = []
            for x in range(0, collide_layer.pixel_width,
                self.levelMap.tilewidth):
                current_grid.append(collide_layer.decoded_content[id_index])
                id_index += 1
            self.collide_grid.append(current_grid)
        # Flip for the y-axis again.
        self.collide_grid.reverse()
#        self._print_collide_grid()

    def load(self, filename):
        if not filename:
            return
        self.levelMap = tiledtmxloader.TileMapParser().parse_decode(filename)
        self.levelMap.load(tiledtmxloader.ImageLoaderPyglet())

        groupCount = 0
        for layer in self.levelMap.layers[:]:
            if not layer.visible:
                continue
            self.groups.append(pyglet.graphics.OrderedGroup(groupCount))
            groupCount += 1
            # DR0ID put his indices into one long array instead of a 2D array.
            id_index = 0
            # Reversed for pyglet's y-perspective.
            for y in range(layer.pixel_height - self.levelMap.tileheight,
                -self.levelMap.tileheight, -self.levelMap.tileheight):
                for x in range(0, layer.pixel_width, self.levelMap.tilewidth):
                    image_id = layer.decoded_content[id_index]
                    id_index += 1
                    if image_id:
                        off_x, off_y, image_file = \
                            self.levelMap.indexed_tiles[image_id]
                        self.sprites.append(pyglet.sprite.Sprite(image_file, x,
                            y, batch=self.batch, group=self.groups[-1]))
        if not len(self.groups):
            # We'll need a single group regardless.
            self.groups.append(pyglet.graphics.OrderedGroup(0))
        self._make_collide_grid()
        self.mapWidth = self.levelMap.layers[0].width
        self.mapHeight = self.levelMap.layers[0].height

    def get_at(self, col, row):
        """Safely retrieve a tile from the level array.

        row and col are expected to be floats so that the formula may catch the
        case where row or col is negative but greater than -1.

        :rtype: Tile reference.
        :return: The tile at row, col. If invalid, returns a default tile.
        """

        if row < 0 or col < 0:
            # Because negative list indices are legal in Python, they must be
            # checked beforehand as a special case.
            return TPROP_THICK
        try:
            return self.collide_grid[int(col)][int(row)]
        except IndexError:
            # x or y was beyond self.collide_grid's size.
            return TPROP_THICK

    def flip_y(self, val):
        """A convenience method for flipping a y-axis variable for pyglet."""
        return self.levelMap.layers[0].pixel_height - val - 32

    def add_extraneous_tiles(self, count=50):
        """Optional method used for testing lag through rendering.

        The tiles don't work collision in any way.
        """
        off_x, off_y, image_file = self.levelMap.indexed_tiles[1]
        for x in range(count):
            self.sprites.append(pyglet.sprite.Sprite(image_file, x=x, y=0,
                batch=self.batch, group=self.groups[0]))

    def post_collide(self, obj=None):
        """Unneeded method only in existance to be called.

        Normal damaging objects have this method. Those objects may have their
        post_collide methods called. The level may deal damage through damaging
        tiles.
        """
        pass

    def draw(self, x, y):
        glPushMatrix()
        # 160 = camera.halfVisibleX * 32
        # 120 = camera.halfVisibleY * 32
        glTranslatef(x + 160.0, y + 120.0, 0.0)
        self.batch.draw()
        glPopMatrix()
